package com.piggy.logi.sys.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.piggy.common.core.utils.PageUtils;
import com.piggy.common.core.web.page.PagePlus;
import com.piggy.common.core.web.page.TableDataInfo;

import com.piggy.logi.sys.api.bo.LogiPriceSegEditBo;
import com.piggy.logi.sys.api.bo.LogiPriceSegQueryBo;
import com.piggy.logi.sys.api.vo.LogiPriceSegExtVo;
import com.piggy.logi.sys.domain.LogiPricePlan;
import com.piggy.logi.sys.domain.LogiPriceSeg;
import com.piggy.logi.sys.enums.FeeTypeEnum;
import com.piggy.logi.sys.mapper.LogiPriceSegMapper;
import com.piggy.logi.sys.service.ILogiPricePlanService;
import com.piggy.logi.sys.service.ILogiPriceSegService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 分段价格Service业务层处理
 *
 * @author piggy
 * @date 2023-08-14
 */
@Slf4j
@Service
public class LogiPriceSegServiceImpl extends ServiceImpl<LogiPriceSegMapper, LogiPriceSeg> implements ILogiPriceSegService {

    @Resource
    private ILogiPricePlanService pricePlanService;

    @Override
    public LogiPriceSegExtVo queryById(Long id){
        return getVoById(id, LogiPriceSegExtVo.class);
    }

    @Override
    public TableDataInfo<LogiPriceSegExtVo> queryPageList(LogiPriceSegQueryBo bo) {
        PagePlus<LogiPriceSeg, LogiPriceSegExtVo> result = pageVo(PageUtils.buildPagePlus(bo, true), buildQueryWrapper(bo), LogiPriceSegExtVo.class);
        return PageUtils.buildDataInfo(result);
    }

    @Override
    public List<LogiPriceSegExtVo> queryList(LogiPriceSegQueryBo bo) {
        return listVo(buildQueryWrapper(bo), LogiPriceSegExtVo.class);
    }

    private LambdaQueryWrapper<LogiPriceSeg> buildQueryWrapper(LogiPriceSegQueryBo bo) {
        LambdaQueryWrapper<LogiPriceSeg> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getId() != null, LogiPriceSeg::getId, bo.getId());
        lqw.eq(bo.getPlanId() != null, LogiPriceSeg::getPlanId, bo.getPlanId());
        lqw.eq(bo.getFeeMode() != null, LogiPriceSeg::getFeeMode, bo.getFeeMode());
        lqw.eq(bo.getFloatStart() != null, LogiPriceSeg::getFloatStart, bo.getFloatStart());
        lqw.eq(bo.getFloatEnd() != null, LogiPriceSeg::getFloatEnd, bo.getFloatEnd());
        lqw.eq(bo.getIntStart() != null, LogiPriceSeg::getIntStart, bo.getIntStart());
        lqw.eq(bo.getIntEnd() != null, LogiPriceSeg::getIntEnd, bo.getIntEnd());
        lqw.eq(bo.getPrice() != null, LogiPriceSeg::getPrice, bo.getPrice());
        lqw.orderByAsc(LogiPriceSeg::getSort);
        return lqw;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LogiPriceSegExtVo insertByAddBo(LogiPriceSegEditBo bo) {
        LogiPriceSeg add = BeanUtil.toBean(bo, LogiPriceSeg.class);
        save(add);
        return queryById(add.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LogiPriceSegExtVo updateByEditBo(LogiPriceSegEditBo bo) {
        LogiPriceSeg update = BeanUtil.toBean(bo, LogiPriceSeg.class);
        updateById(update);
        return queryById(update.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchByAddBo(List<LogiPriceSegEditBo> boList) {
        if (CollUtil.isEmpty(boList)) {
            return false;
        }
        this.remove(Wrappers.<LogiPriceSeg>lambdaQuery().eq(LogiPriceSeg::getPlanId, boList.get(0).getPlanId()));
        return saveBatch(BeanUtil.copyToList(boList, LogiPriceSeg.class));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchByUpdateBo(List<LogiPriceSegEditBo> boList) {
        if (CollUtil.isEmpty(boList)) {
            return false;
        }
        this.remove(Wrappers.<LogiPriceSeg>lambdaQuery().eq(LogiPriceSeg::getPlanId, boList.get(0).getPlanId()));
        return this.saveBatch(BeanUtil.copyToList(boList, LogiPriceSeg.class));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByPlanId(Long planId) {
        return baseMapper.delete(Wrappers.<LogiPriceSeg>lambdaQuery().eq(LogiPriceSeg::getPlanId, planId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return removeByIds(ids);
    }

    private BigDecimal calcAmountFee(Long planId, String feeMode, BigDecimal amount) {
        LogiPricePlan plan = pricePlanService.getById(planId);

        List<LogiPriceSegExtVo> segVos = queryList(new LogiPriceSegQueryBo().setPlanId(planId).setFeeMode(feeMode));
        if (CollUtil.isEmpty(segVos)) {
            return BigDecimal.ZERO;
        }

        for (LogiPriceSegExtVo t : segVos) {
            /*if (FeeTypeEnum.isMileage(t.getFeeMode())) {
                continue;
            }*/
            if (t.getFloatEnd().compareTo(BigDecimal.ZERO) <= 0) {
                t.setFloatEnd(BigDecimal.TEN.pow(BigDecimal.TEN.intValue()));
            }
            if (amount.compareTo(t.getFloatEnd()) > 0) {
                /*if (t.getSort() == 0) {
                    t.setAmount(t.getFloatEnd().subtract(t.getFloatStart()));
                } else {
                    t.setAmount(t.getFloatEnd().subtract(t.getFloatStart()));
                }*/
                t.setAmount(t.getFloatEnd().subtract(t.getFloatStart()));
            } else {
                if (t.getSort() ==0) {
                    t.setAmount(amount.compareTo(t.getFloatStart())<=0 ? t.getFloatStart() : amount);
                } else {
                    t.setAmount(amount.subtract(t.getFloatStart()));
                }
                amount = BigDecimal.ZERO;
            }
            if (t.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
                t.setAmount(BigDecimal.ZERO);
            }
            if (amount.compareTo(BigDecimal.ZERO) <= 0) {
                break;
            }
        }

        BigDecimal fee = BigDecimal.ZERO;
        for (LogiPriceSegExtVo t : segVos) {
            if (FeeTypeEnum.isVolume(t.getFeeMode())) {
                fee = fee.add(t.getAmount().multiply(t.getPrice()));
                fee = (fee.multiply(BigDecimal.valueOf(plan.getWaterProof()))).divide(BigDecimal.TEN.multiply(BigDecimal.TEN));
            } else if (FeeTypeEnum.isWeight(t.getFeeMode())) {
                fee = fee.add(t.getAmount().multiply(t.getPrice()));
            } else if (FeeTypeEnum.isMileage(t.getFeeMode())) {
                fee = fee.add(t.getAmount().multiply(t.getPrice()));
            } else {
                fee = fee.add(t.getAmount().multiply(t.getPrice()));
            }
        }

        return fee;
    }

    @Override
    public BigDecimal calcAmountFee(Long planId, BigDecimal amount) {
        LogiPricePlan plan = pricePlanService.getById(planId);

        if (FeeTypeEnum.isMileage(plan.getFeeMode()) &&
                FeeTypeEnum.isMileage(plan.getAddFeeMode()) &&
                FeeTypeEnum.isMileage(plan.getAddFeeMode1())) {
            return BigDecimal.ZERO;
        }

        return calcAmountFee(planId, plan.getFeeMode(), amount);
    }

    @Override
    public BigDecimal calcMileFee(Long planId, BigDecimal miles) {
        LogiPricePlan plan = pricePlanService.getById(planId);

        if (!FeeTypeEnum.isMileage(plan.getFeeMode()) &&
                !FeeTypeEnum.isMileage(plan.getAddFeeMode()) &&
                !FeeTypeEnum.isMileage(plan.getAddFeeMode1())) {
            return BigDecimal.ZERO;
        }

        /*List<LogiPriceSegExtVo> segVos = queryList(new LogiPriceSegQueryBo().setPlanId(planId));
        if (CollUtil.isEmpty(segVos)) {
            return BigDecimal.ZERO;
        }
        for (LogiPriceSegExtVo t : segVos) {
            if (!FeeTypeEnum.isMileage(t.getFeeMode())) {
                continue;
            }
            if (t.getFloatEnd().compareTo(BigDecimal.ZERO) <= 0) {
                t.setFloatEnd(BigDecimal.TEN.pow(8));
            }
            if (miles.compareTo(t.getFloatEnd()) > 0) {
                *//*if (t.getSort() == 0) {
                    t.setAmount(t.getFloatEnd().subtract(t.getFloatStart()));
                } else {
                    t.setAmount(t.getFloatEnd().subtract(t.getFloatStart()));
                }*//*
                t.setAmount(t.getFloatEnd().subtract(t.getFloatStart()));
            } else {
                if (t.getSort() ==0 && t.getFloatStart().compareTo(miles)<=0) {
                    t.setAmount(miles);
                } else {
                    t.setAmount(miles.subtract(t.getFloatStart().add(BigDecimal.ONE)));
                }
            }
            if (t.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
                t.setAmount(BigDecimal.ZERO);
            }
            if (miles.compareTo(BigDecimal.ZERO) <= 0) {
                break;
            }
        }

        BigDecimal fee = BigDecimal.ZERO;
        for (LogiPriceSegExtVo t : segVos) {
            fee = fee.add(t.getAmount().multiply(t.getPrice()));
        }
        return fee;*/
        return calcAmountFee(planId, FeeTypeEnum.MILEAGE.getType(), miles);
    }

    @Override
    public BigDecimal calcWeightAndVolFee(Long planId, BigDecimal weight, BigDecimal vol) {
        LogiPricePlan plan = pricePlanService.getById(planId);

        if (!FeeTypeEnum.isMileage(plan.getFeeMode()) &&
                !FeeTypeEnum.isMileage(plan.getAddFeeMode()) &&
                !FeeTypeEnum.isMileage(plan.getAddFeeMode1())) {
            return BigDecimal.ZERO;
        }

        BigDecimal fee = calcAmountFee(planId, FeeTypeEnum.VOLUME.getType(), vol);
        return fee.add(calcAmountFee(planId, FeeTypeEnum.WEIGHT.getType(), vol));
    }

    @Override
    public BigDecimal calcWeigthMultiMileFee(Long planId, BigDecimal weight, BigDecimal miles) {
        List<LogiPriceSegExtVo> minWeights = queryList(new LogiPriceSegQueryBo().setPlanId(planId).setFeeMode(FeeTypeEnum.WEIGHT.getType()));
        if (CollUtil.isEmpty(minWeights)) {
            return BigDecimal.ZERO;
        }
        List<LogiPriceSegExtVo> segVos = queryList(new LogiPriceSegQueryBo().setPlanId(planId).setFeeMode(FeeTypeEnum.MILEAGE.getType()));
        if (CollUtil.isEmpty(segVos)) {
            return BigDecimal.ZERO;
        }
        weight = weight.compareTo(minWeights.get(0).getFloatStart())<=0 ? minWeights.get(0).getFloatStart() : weight;
        return weight.multiply(calcAmountFee(planId, FeeTypeEnum.MILEAGE.getType(), miles)).setScale(3, RoundingMode.HALF_UP);
    }

    private BigDecimal calcMilePrice(Long planId, String feeMode, BigDecimal miles, BigDecimal weight) {
        LogiPricePlan plan = pricePlanService.getById(planId);

        List<LogiPriceSegExtVo> segVos = queryList(new LogiPriceSegQueryBo().setPlanId(planId).setFeeMode(feeMode));
        if (CollUtil.isEmpty(segVos)) {
            return BigDecimal.ZERO;
        }

        for (LogiPriceSegExtVo t : segVos) {
            if (t.getFloatEnd().compareTo(BigDecimal.ZERO) <= 0) {
                t.setFloatEnd(BigDecimal.TEN.pow(BigDecimal.TEN.intValue()));
            }
            if (miles.compareTo(t.getFloatEnd()) > 0) {
                t.setAmount(t.getFloatEnd().subtract(t.getFloatStart()));
            } else {
                if (t.getSort() ==0) {
                    t.setAmount(miles.compareTo(t.getFloatStart())<=0 ? t.getFloatStart() : miles);
                } else {
                    t.setAmount(miles.subtract(t.getFloatStart()));
                }
                miles = BigDecimal.ZERO;
            }
            if (t.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
                t.setAmount(BigDecimal.ZERO);
            }
            if (miles.compareTo(BigDecimal.ZERO) <= 0) {
                break;
            }
        }

        BigDecimal fee = BigDecimal.ZERO;
        for (LogiPriceSegExtVo t : segVos) {
            if (FeeTypeEnum.isMileage(t.getFeeMode())) {
                if (t.getAmount().compareTo(BigDecimal.ZERO) > 0) {
                    t.setAmount(new BigDecimal("1"));
                }
                fee = fee.add(weight.multiply(t.getPrice()).multiply(t.getAmount()));
            } else if (FeeTypeEnum.isWeightMultiMileage(t.getFeeMode())) {
                if (t.getAmount().compareTo(BigDecimal.ZERO) > 0) {
                    t.setAmount(new BigDecimal("1"));
                }
                fee = fee.add(weight.multiply(t.getPrice()).multiply(t.getAmount()));
            } else {
                fee = fee.add(BigDecimal.ZERO);
            }
        }

        return fee.setScale(3, RoundingMode.HALF_UP);
    }

    public static void main(String[] args) {
        BigDecimal value = new BigDecimal("123.3214434");
        System.out.println(value.setScale(3, BigDecimal.ROUND_HALF_UP));
    }
}
